package controller;

import itdb1.commun.*;
import itdb1.commun.Message;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.ArrayBlockingQueue;

import network.HeartBeatGenerator;
import network.LocalNetworkSettings;
import network.UDPTransmitter;
import network.UploaderTCPClient;

import view.ChatGUI;
import guimessage.*;

public class GUIController extends GenericController {

	private static GUIController instance = null;
	
	public ArrayBlockingQueue<RawGUISignal> messages;
	public ChatGUI gui;
	
	private UDPServerController udpserverCtrl;
	private TCPServerController tcpserverCtrl;
	private HeartBeatGenerator heartbeatGenerator;
	
	private GUIController() {
		super();
		messages = new ArrayBlockingQueue<RawGUISignal>(20);
	}

	public static GUIController getInstance()
	{
		if(instance == null)
			instance = new GUIController();
		
		return instance;
	}
	
	@Override
	public void run() {
		this.gui = new ChatGUI();
		
		RawGUISignal signal = null;
		
		writeDebugScreen("GUIController > launched\n");
		
		while(true)
		{
			try
			{
				signal = messages.take();
			}
			catch(InterruptedException e)
			{}
			
			if(signal.getClass() == Connect.class)
			{
				processConnect((Connect) signal);
			}
			else if(signal.getClass() == Disconnect.class)
			{
				processDisconnect((Disconnect) signal);
			}
			else if(signal.getClass() == FileUpload.class)
			{
				processFileUpload((FileUpload) signal);
			}
			else if(signal.getClass() == guimessage.Message.class)
			{
				processMessage((guimessage.Message) signal);
			}
			else if(signal.getClass() == guimessage.NotifNetwork.class)
			{
				processNotif((guimessage.NotifNetwork) signal);
			}
			else if(signal.getClass() == guimessage.RemoteDisconnect.class)
			{
				processRemoteDisconnect((guimessage.RemoteDisconnect) signal);
			}			
			else
			{
				writeDebugScreen("GUIController > bad message received from GUI !\n");
			}
		}
	}

	private void processConnect(Connect signal)
	{
		try
		{
			// Launching of TCP and UDP controllers which are going to launch themselves the UDP and TCP servers
			udpserverCtrl = UDPServerController.getInstance();
			( new Thread(udpserverCtrl) ).start();
			
			tcpserverCtrl = TCPServerController.getInstance();
			( new Thread(tcpserverCtrl) ).start();
			
			new UDPTransmitter( new Hello(signal.getLocaluser(), InetAddress.getByName("255.255.255.255")), LocalNetworkSettings.REMOTE_UDP_SERVER_PORT);
			
			if(LocalNetworkSettings.heartBeatActivation)
			{
				heartbeatGenerator = new HeartBeatGenerator();
				( new Thread(heartbeatGenerator) ).start();
			}
		}
		catch(UnknownHostException e) {// On aura jamais cette erreur de toute façon
			e.printStackTrace();
		}
	}
	
	private void killServers()
	{	
		if(LocalNetworkSettings.heartBeatActivation)
			heartbeatGenerator.kill();
		
		udpserverCtrl.kill();
		tcpserverCtrl.kill();
	}
	
	private void processDisconnect(Disconnect signal)
	{
		try
		{
			new UDPTransmitter( new Bye(InetAddress.getByName("255.255.255.255")), LocalNetworkSettings.REMOTE_UDP_SERVER_PORT );
			
			killServers();
			gui.getChatScreen().cleanChatScreen();
			this.contacts.clear();
		}
		catch(UnknownHostException e)
		{// On aura jamais cette erreur de toute façon
		}
	}
	
	private void processFileUpload(FileUpload signal)
	{
		try
		{
			( new Thread( new UploaderTCPClient(signal, LocalNetworkSettings.REMOTE_TCP_SERVER_PORT))).start();
		}
		catch(IOException ioException)
		{
			ioException.printStackTrace();
		}
	}
	
	private void processMessage(guimessage.Message signal)
	{
		//this.writeDiscussionTab(signal);
		new UDPTransmitter( new itdb1.commun.Message(signal.getMessage(), signal.getContact().getIpAddr()), LocalNetworkSettings.REMOTE_UDP_SERVER_PORT );
	}
	
	private void processRemoteDisconnect(guimessage.RemoteDisconnect signal)
	{
		if( this.contacts.present(signal.getFrom()) )
		{
			writeDeparture(signal.getFrom());
			
			this.contacts.remove(signal.getFrom());
		}
	}
	
	private void processNotif(NotifNetwork signal)
	{
		if( this.contacts.present(signal.getFrom()) )
		{
			writeNotif(signal.getFrom(), signal.getMessage());
		}
	}
	
	public void writeDebugScreen(String s)
	{
		gui.getDebugScreen().write(s);
	}
	
	public void writeDiscussionTab(Message m)
	{
		Contact c = contacts.get(m.getFromIp());
		String s = c.getUsername() + "@" + c.getIpAddr().getHostAddress() + " > " + m.getMessage() + "\n";
		
		gui.getChatScreen().writeDiscussion(s , m.getFromIp());
	}
	
	public void writeDeparture(InetAddress ip)
	{
		String s = "The contact left the discussion !";
		gui.getChatScreen().notifDeparture(s, ip);
	}
	
	public void writeNotif(InetAddress ip, String message)
	{
		gui.getChatScreen().notidTransfer(message, ip);
	}
	
	public boolean AskAcknowledgementDownload(InetAddress who, FileDescription dscrpt)
	{
		if(contacts.present(who))
		{
			Contact c = contacts.get(who);
			return gui.getChatScreen().askUser(c.username + " propose you to download " + dscrpt.getName() +" (" + dscrpt.getSize() + "KB)", who);
		}
		else // si on  ne sait pas qui c'est, on ne le montre même pas à l'utilisateur
			return false;
	}
}
